/*
 * Copyright (C) 2000-2005 Philips Electronics N.V.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file gnu_license.txt.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
 * 
 */
#include <string.h>
#include <gmp.h>
#include <front/text.h>
#include <front/mem.h>

#include "fe_flags.h"
#include "cfrontPrint.h"
#include "init.h"
#include "strings.h"
#include "type.h"
#include "ast.h"
#include "errors.h"
#include "attribute.h"
#include "semantics.h"

/* List node removal */
struct s_copy_cfront_TraverseTable cfront_RemoveTable;
int cfront_RemoveOffset;

/* List node replace */
struct s_copy_cfront_TraverseTable cfront_ReplaceTable;
int cfront_ReplaceOffset;

/* Pre: true */
static struct s_cfront_TraverseTable give_statement;
static struct s_cfront_TraverseTable give_function;
static struct s_cfront_TraverseTable link_declarations;
static struct s_cfront_TraverseTable link_type_names;
static struct s_cfront_TraverseTable link_external_decls;
static struct s_void_cfront_TraverseTable eval_constants;
static struct s_cfront_TraverseTable propagate_storage_class;
static struct s_cfront_TraverseTable propagate_function_specifier;
static struct s_cfront_TraverseTable link_enumerators;
static struct s_cfront_TraverseTable link_initializers;
static struct s_cfront_TraverseTable mark_initializers;
static struct s_cfront_TraverseTable link_breaks;
static struct s_cfront_TraverseTable link_continues;
static struct s_cfront_TraverseTable link_cases;
static struct s_cfront_TraverseTable mark_function_decls;
static struct s_cfront_TraverseTable give_FuncDecl_body;
static struct s_cfront_TraverseTable expression_info;
static struct s_void_cfront_TraverseTable clear_links;
static struct s_void_cfront_TraverseTable link_callers;
static struct s_cfront_TraverseTable funcaddr_taken;

/* Pre: applied_actions */
static struct s_void_cfront_TraverseTable link_tags;
static struct s_void_cfront_TraverseTable declarator_info;

/* Pre: applied_actions, mark_function_decls, propagate_storage_class */
static struct s_void_cfront_TraverseTable link_declarators;

/* Initialize the above traversals */
static void init_traversal(void);

/* symbols with a generation number below bottom are internal, they
   should not be cleaned */
static int symbol_table_bottom;

/* Support for -E option, preprocess only, then output to stdout and exit */
static Bool pp_only = FALSE; 
/* ----------------------------------------------------------------------------
 * Evaluate constant
 */
static void
eval_constants_NumExpr(NumExpr e)
{
  char *str;
  int s, l;
  int base = 0;

  if (NumExpr_val(e)) return;
  str = fe_strdup (NumExpr_i(e));
  for (l=strlen(str); l>0; ) {
    l--;
    if (strchr("0123456789ABCDEFabcdef", str[l])) break;
  }
  str[l+1]=0;

  NumExpr_val(e) = fe_malloc (sizeof (mpz_t));
  s = mpz_init_set_str (*NumExpr_val(e), str, base);
  if (s!=0) {
    invalid_integer_constant (e);
    mpz_clear (*NumExpr_val(e));
  }
}

static void
eval_constants_FloatExpr(FloatExpr e)
{
  char *str;
  int s;

  if (FloatExpr_val(e)) return;
  str = fe_strdup (FloatExpr_f(e));

  FloatExpr_val(e) = fe_malloc (sizeof (mpf_t));
#warning Todo: check whether GMP accepts all C float constants
  /* once enabled check eval.c */
  s = mpf_init_set_str (*FloatExpr_val(e), str, 10);
  if (s!=0) {
    invalid_float_constant (e);
    mpf_clear (*FloatExpr_val(e));
  }
}

static void
eval_constants_CharExpr(CharExpr e)
{
  if (CharExpr_val(e)) return;
  CharExpr_val(e) = fe_malloc (sizeof (mpz_t));
  mpz_init_set_si(*CharExpr_val(e), CharExpr_c(e)[0]);
}

static void
eval_constants_WideCharExpr(WideCharExpr e)
{
  wchar_t v;
  int r;
  
  if (WideCharExpr_val(e)) return;
  r = mbtowc (&v, WideCharExpr_c(e), WideCharExpr_size(e));
  if (r<0) {
    invalid_wide_character (e);
    return;
  }
  if (r==0) v=0;
    
  WideCharExpr_val(e) = fe_malloc (sizeof (mpz_t));
  mpz_init_set_si(*WideCharExpr_val(e), v);
}

/* ----------------------------------------------------------------------------
 * Clear links in functionDefinition
 */
static void
clear_links_functionDefinition (functionDefinition f)
{
  functionDefinition_callers(f) = NULL;
}

/* ----------------------------------------------------------------------------
 * Link declarations to Id_Decl's
 */
static declaration 
link_declarations_declarator (declarator d, declaration decl)
{
  if (d) declarator_my_decl(d) = decl;
  return decl;
}

/* ----------------------------------------------------------------------------
 * Link type_name to Id_Decl's
 */
static type_name 
link_type_names_declarator (declarator d, type_name tn)
{
  if (d) declarator_my_type_name(d) = tn;
  return tn;
}

/* ----------------------------------------------------------------------------
 * Link externalDeclarations to declaration
 */
static externalDeclaration
link_external_decls_declaration (declaration me, externalDeclaration ext)
{
  declaration_cur_external (me) = ext;
  return ext;
}

static externalDeclaration
link_external_decls_functionDefinition (functionDefinition me,
                                        externalDeclaration ext)
{
  functionDefinition_cur_external (me) = ext;
  return NULL;
}

/* ----------------------------------------------------------------------------
 * Cross link statement to expression and declarator
 */
static statement
give_statement_get(statement me, statement parent)
{
  if (me) statement_parent(me) = parent;
  return me;
}

static statement
give_statement_declarator(declarator me, statement stat)
{
  if (me) declarator_cur_statement(me) = stat;
  return stat;
}

static statement
give_statement_expression(expression me, statement stat)
{
  if (me) expression_cur_statement(me) = stat;
  return stat;
}

/* ----------------------------------------------------------------------------
 * Cross link function to statement, expression, and declarator
 */
static functionDefinition
give_function_expression(expression me, functionDefinition func)
{
  if (me) expression_cur_func(me) = func;
  return func;
}

static functionDefinition
give_function_statement(statement me, functionDefinition func)
{
  if (me) statement_cur_func(me) = func;
  return func;
}

static functionDefinition
give_function_declarator(declarator me, functionDefinition func)
{
  if (me) declarator_cur_func(me) = func;
  return func;
}

/* ----------------------------------------------------------------------------
 * Link forward declaration and uses of structs, unions, and enums to
 * their definition (3.5.2.3)
 */

static Bool
same_block (Scope a, Scope b)
{
  if (a==b) return TRUE;
  if (!Sub_scope (a, b)) {
    if (Scope_tag (b)==compound_statementScope_kind) return FALSE;
    if (Scope_tag (b)==parameter_listScope_kind) return FALSE;
    b = Scope_next (b);
  }
  if (!Sub_scope (b, a)) {
    if (Scope_tag (a)==compound_statementScope_kind) return FALSE;
    if (Scope_tag (a)==parameter_listScope_kind) return FALSE;
    a = Scope_next (a);
  }
  return same_block (a, b);
}

static void
link_tags_declaration_specifier (declaration_specifier me)
{
  declaration_specifier_definition (me) = NULL;
}

static void
link_tags_StructDef (StructDef me)
{
  Scope scope = Struct_Scope(me);
  declaration_specifier next;
  declaration_specifier pre;
  Bind bind;

  if (Scope_tag (Struct_Scope (me)) == parameter_listScope_kind) {
    tag_declared_in_parameter (me, StructDef_id (me)->name);
  }
  
  bind = RetrieveBind (StructDef_id (me), scope,
                       declaration_specifier__num(me), TagNameSpace,
                       Bind_cfront_declaration_specifier_kind);
  if (!bind) return;
  pre = Bind_object (bind);
  assert (pre != me);
  assert (Bind_loc(bind) == declarator__num(pre));
  
  switch (declaration_specifier_tag (pre)) {
  case StructDecl_kind: case StructDef_kind:
    break;
  default:
    previous_declaration_not_struct (me, Ident_name (StructDef_id (me)), pre);
    return;
  }
  /* pre is a forward declaration iff it's in the same block */
  if (!same_block (scope, declaration_specifier_scope (pre))) {
    tag_already_declared_in_outer_scope (me, Ident_name (StructDef_id (me)), pre);
    return;
  }
  for (next = pre; next; pre = next) {
    next = declaration_specifier_definition (pre);
    if (declaration_specifier_tag (pre) != StructDecl_kind) {
      tag_already_declared (me, Ident_name (StructDef_id (me)), pre);
      return;
    }
    declaration_specifier_definition (pre) = me;
  }
}

static void
link_tags_StructDecl (StructDecl me)
{
  Scope scope = Struct_Scope(me);
  declaration_specifier pre;
  Bind bind;

  bind = RetrieveBind (StructDecl_id (me), scope,
                       declaration_specifier__num(me), TagNameSpace,
                       Bind_cfront_declaration_specifier_kind);
  if (!bind) return;
  pre = Bind_object (bind);
  assert (pre != me);
  assert (Bind_loc(bind) == declarator__num(pre));
  
  switch (declaration_specifier_tag (pre)) {
  case StructDecl_kind: case StructDef_kind:
    declaration_specifier_definition (me) = pre;
    break;
  default:
    previous_declaration_not_struct (me, Ident_name (StructDecl_id (me)));
  }
}

static void
link_tags_UnionDef (UnionDef me)
{
  Scope scope = Struct_Scope(me);
  declaration_specifier next;
  declaration_specifier pre;
  Bind bind;

  if (Scope_tag (Struct_Scope (me)) == parameter_listScope_kind) {
    tag_declared_in_parameter (me);
  }
  
  bind = RetrieveBind (UnionDef_id (me), scope,
                       declaration_specifier__num(me), TagNameSpace,
                       Bind_cfront_declaration_specifier_kind);
  if (!bind) return;
  pre = Bind_object (bind);
  assert (pre != me);
  assert (Bind_loc(bind) == declarator__num(pre));
  
  switch (declaration_specifier_tag (pre)) {
  case UnionDecl_kind: case UnionDef_kind:
    break;
  default:
    previous_declaration_not_union (me, Ident_name (UnionDef_id (me)));
    return;
  }
  /* pre is a forward declaration iff it's in the same block */
  if (!same_block (scope, declaration_specifier_scope (pre))) {
    tag_already_declared_in_outer_scope (me, Ident_name (StructDef_id (me)), pre);
    return;
  }
  for (next = pre; next; pre = next) {
    next = declaration_specifier_definition (pre);
    if (declaration_specifier_tag (pre) != UnionDecl_kind) {
      tag_already_declared (me, Ident_name (UnionDef_id (me)), pre);
      return;
    }
    declaration_specifier_definition (pre) = me;
  }
}

static void
link_tags_UnionDecl (UnionDecl me)
{
  Scope scope = Struct_Scope(me);
  declaration_specifier pre;
  Bind bind;

  bind = RetrieveBind (UnionDecl_id (me), scope,
                       declaration_specifier__num(me), TagNameSpace,
                       Bind_cfront_declaration_specifier_kind);
  if (!bind) return;
  pre = Bind_object (bind);
  assert (pre != me);
  assert (Bind_loc(bind) == declarator__num(pre));
  
  switch (declaration_specifier_tag (pre)) {
  case UnionDecl_kind: case UnionDef_kind:
    declaration_specifier_definition (me) = pre;
    break;
  default:
    previous_declaration_not_union (me, Ident_name (UnionDecl_id (me)));
  }
}

static void
link_tags_declaration (declaration me)
{ /* Post action */
  List_declaration_specifier specs;
  declaration_specifier spec;
  declaration_specifier def;
  
  if (!me || declaration_decls (me)) return;
  /* for example: "union tag;" or "struct tag;" */
  specs = declaration_specifiers_specs (declaration_specs (me));
  spec = List_declaration_specifier_data (specs);
  if (declaration_specifier_tag (spec) == StorageClass_kind) {
    if (StorageClass_sc (spec) != Typedef) {
      superfluous_storage_class (spec, Text_declaration_specifier (spec));
    }
    specs = List_declaration_specifier_next (specs);
    spec = List_declaration_specifier_data (specs);
  }
  def = declaration_specifier_definition (spec);
  if ((declaration_specifier_tag (spec) == Enum_kind ||
       declaration_specifier_tag (spec) == StructDecl_kind ||
       declaration_specifier_tag (spec) == StructDef_kind ||
       declaration_specifier_tag (spec) == UnionDef_kind ||
       declaration_specifier_tag (spec) == UnionDecl_kind) &&
      !List_declaration_specifier_next (specs)) {
    /* declaration of a tag (3.5.2.3):
         ... specifies a structure or union type and declares a tag,
         both visible only within the scope in which the declaration
         occurs.  It specifies a new type distinct from any type with
         the same tag in an enclosing scope (if any).
     */
    
    if (def) {
      Scope spec_scope = Struct_Scope (spec);
      Scope def_scope  = Struct_Scope (def);
      if (spec_scope != def_scope) {
        declaration_specifier_definition (spec) = NULL;
      }
    }
  } else {
    invalid_declaration (me);
  }
}

static void
link_tags_Enum (Enum me)
{
  Enum next;
  Enum pre;
  Bind bind;

  bind = RetrieveBind (Enum_id (me), declaration_specifier_scope(me),
                       declaration_specifier__num(me), TagNameSpace,
                       Bind_cfront_declaration_specifier_kind);
  if (!bind) {
    if (!Enum_enums (me)) forward_decl_of_enum_forbidden (me);
    return;
  }
  pre = Bind_object (bind);
  assert (pre != me);
  assert (Bind_loc(bind) == declarator__num(pre));
  
  if (declaration_specifier_tag (pre) != Enum_kind) {
    previous_declaration_not_enum (me, Ident_name (Enum_id (me)));
    return;
  }
  if (Enum_enums (me)) {
    for (next = pre; next; pre = next) {
      next = declaration_specifier_definition (pre);
      if (Enum_enums (pre)) {
        tag_already_declared (me, Ident_name (Enum_id (me)), pre);
        return;
      }
      declaration_specifier_definition (pre) = me;
    }
  } else declaration_specifier_definition (me) = pre;
}

/* ----------------------------------------------------------------------------
 * Link declarators of the same name.  (ANSI-C 3.1.2.2)
 */
static void
link_declarators_Id_Decl (Id_Decl me)
{
  Id_Decl pre;
  Bind bind;
  
  bind = RetrieveBind (Id_Decl_id (me), declarator_scope(me),
                       declarator__num(me), DefaultNameSpace,
                       Bind_cfront_declarator_kind);
  if (!bind) return;
  
  pre = Bind_object (bind);
  assert (pre != me);
  assert (Bind_loc(bind) == declarator__num(pre));
  
  if (declarator_tag (pre) == Par_Id_Decl_kind) {
    Id_Decl_first (me) = pre;
    Par_Id_Decl_decl (pre) = me;
    return;
  }
  if (declarator_tag (pre) != Id_Decl_kind) return;
  
  if (Id_Decl_first (pre)) Id_Decl_first (me) = Id_Decl_first (pre);
  else                     Id_Decl_first (me) = pre;

  /* check linkages */
  if (has_internal_linkage (pre)) {
    if (has_internal_linkage (me)) return;
    if (has_external_linkage (me)) {
      previously_declared_static (me, Ident_name (Id_Decl_id (me)));
    }
  } else if (has_external_linkage (pre)) {
    if (has_external_linkage (me)) return;
    if (has_internal_linkage (me)) {
      previously_declared_extern (me, Ident_name (Id_Decl_id (me)));
    }
  } else {
    if (declarator_scope (me)==declarator_scope (pre)) {
      #warning Duplicate function declarations should be checked for compatibility (but: requires types).
      if (!Id_Decl_is_function (me) && !Id_Decl_is_function (pre)) {
        already_declared (me, Ident_name (Id_Decl_id (me)));
      }
    }
  }
  Id_Decl_first (me) = NULL;
}

/* ----------------------------------------------------------------------------
 * Link an enumerator to its predecessor in the same enum
 */
static enumerator
link_enumerators_Enum (Enum e, enumerator pred)
{
  NOT_USED(e);
  NOT_USED(pred);
  return NULL;
}

static enumerator
link_enumerators_enumerator(enumerator me, enumerator pred)
{
  enumerator_pred (me) = pred;
  return me;
}

/* ----------------------------------------------------------------------------
 * Mark the initializing expressions as initializers
 */
static Bool
mark_initializers_expression(expression me, Bool init)
{
  if (!me) return init;
  expression_is_initializer (me) = init;
  return init && expression_tag (me) != SizeofExpr_kind;
}

static Bool
mark_initializers_initDeclarator(initDeclarator me, Bool init)
{
  NOT_USED (me);
  NOT_USED (init);
  return TRUE;
}

/* ----------------------------------------------------------------------------
 * Link an Id_Decl to it's initializer, if it has one. 
 */
static declarator
link_initializers_Id_Decl(Id_Decl me, declarator id)
{
  NOT_USED(id);
  return me;
}

static declarator
link_initializers_initDeclarator(initDeclarator me, Id_Decl id)
{
  expression init = initDeclarator_init(me);
  declarator_init_expr (id) = init;
  return NULL;
}

/* ----------------------------------------------------------------------------
 * Propagate the storage class to the declarators
 */
static storage_class_specifier 
propagate_storage_class_declarator(declarator me, storage_class_specifier scs)
{
  if (!me) return scs;
  declarator_storage_class (me) = scs;
  return scs;
}

static storage_class_specifier 
propagate_storage_class_reset (void *me,
                               storage_class_specifier scs)
{
  NOT_USED (me);
  NOT_USED (scs);
  return NoSCS;
}

static List_Int storage_class_specifier_stack;

static storage_class_specifier 
propagate_storage_class_push (void *me,
                              storage_class_specifier scs)
{
  NOT_USED (me);
  FRONT_PREPEND_NODE (scs, storage_class_specifier_stack);
  return NoSCS;
}

static storage_class_specifier 
propagate_storage_class_pop (void *me,
                             storage_class_specifier scs)
{
  NOT_USED (me);
  scs = HEAD(storage_class_specifier_stack);
  storage_class_specifier_stack = TAIL(storage_class_specifier_stack);
  return scs;
}

static storage_class_specifier
propagate_storage_class_storage_class_specifier(storage_class_specifier me,
                                                storage_class_specifier old)
{
  NOT_USED(old);
  return me;
}

/* ----------------------------------------------------------------------------
 * Propagate the function_specifier to the declarators
 */
static declaration_specifier 
propagate_function_specifier_declarator(declarator me, declaration_specifier scs)
{
  if (scs == NULL) return scs;
  switch (declaration_specifier_tag(scs)) {
    case inline_specifier_kind:
    case inline_specifier1_kind:
    case inline_specifier2_kind:
      declarator_is_inline (me) = TRUE;
      break;
    default: break;
  }
  return scs;
}

static declaration_specifier 
propagate_function_specifier_reset (void *me,
                               declaration_specifier scs)
{
  NOT_USED (me);
  NOT_USED (scs);
  return NULL;
}

static declaration_specifier
propagate_function_specifier_function_specifier(declaration_specifier me,
                                                declaration_specifier old)
{
  switch (declaration_specifier_tag(me)) {
    case inline_specifier_kind:
    case inline_specifier1_kind:
    case inline_specifier2_kind:
      return me;
    default: break;
  }
  
  return old;
}

/* ----------------------------------------------------------------------------
 * Link break to the statement it belongs to
 */
static statement
link_breaks_BreakStat(statement me, statement stat)
{
  if (!stat) {
    if (BreakStat_context (me)) return stat;
    invalid_break_context (me);
  }
  BreakStat_context (me) = stat;
  return stat;
}

/* ----------------------------------------------------------------------------
 * Link continue to the statement it belongs to
 */
static statement
link_continues_ContinueStat(statement me, statement stat)
{
  if (!stat) {
    if (ContinueStat_context (me)) return stat;
    invalid_continue_context (me);
  }
  ContinueStat_context (me) = stat;
  return stat;
}

/* ----------------------------------------------------------------------------
 * Link cases and defaults to the switch statement they belong to
 */

static statement
link_cases_CaseStat(statement me, statement stat)
{
  if (!stat) case_label_outside_switch (me);
  CaseStat_switchstat (me) = stat;
  return stat;
}

static statement
link_cases_DefaultStat(statement me, statement stat)
{
  if (!stat) default_label_outside_switch (me);
  DefaultStat_switchstat (me) = stat;
  return stat;
}

/* ----------------------------------------------------------------------------
 * Link function call to functionDefinition
 */
static void link_id_caller (IdExpr f)
{
  declarator d = IdExpr_decl(f);
  if (!d) return;
  functionDefinition func = declarator_cur_func(d);
  if (!func) return;
  function_header h = functionDefinition_head(func);
  if (get_Id_Decl(function_header_decl(h)) != d) return;
  
  FRONT_INSERT (f, functionDefinition_callers(func));
}

/* ----------------------------------------------------------------------------
 * Link function call to functionDefinition
 */
static void link_caller (CallExpr call)
{
  expression f = CallExpr_func(call);

  if (expression_tag(f) != IdExpr_kind) return;
  declarator d = IdExpr_decl(f);
  if (!d) return;
  functionDefinition func = declarator_cur_func(d);
  if (!func) return;
  function_header h = functionDefinition_head(func);
  if (get_Id_Decl(function_header_decl(h)) != d) return;
  
  FRONT_INSERT (call, functionDefinition_callers(func));
}

/* ----------------------------------------------------------------------------
 * mark Id_Decls of functions
 */
static void
mark_function_decls_Id_Decl(Id_Decl me, FuncDecl func)
{
  Id_Decl_is_function (me) = (func!=NULL);
}

/* ----------------------------------------------------------------------------
 * give the body of the functionDefinition to its declarator
 */
static functionDefinition 
give_FuncDecl_body_FuncDecl(FuncDecl me, functionDefinition fd)
{
  if (fd) FuncDecl_body (me) = functionDefinition_body (fd);
  return NULL;
}

/* ----------------------------------------------------------------------------
 * Add information to the declarator about how it's being used
 */
static void
declarator_info_mon_expression (mon_expression me)
{
  expression arg;
  
  if (mon_expression_op(me) != AddrOp) return;
  arg = mon_expression_arg (me);
  if (expression_tag (arg) != IdExpr_kind || !IdExpr_decl (arg)) return;
  Id_Decl_address_taken (IdExpr_decl (arg)) = TRUE;
}

static Bool
is_leaf_call (CallExpr e, Bool b)
{
  if (!b) return b;
  return is_custom_op(CallExpr_func(e));
}

/* ----------------------------------------------------------------------------
 * Add information to the declarator about how it's being used
 */
static void 
declarator_info_functionDefinition (functionDefinition f)
{
  static struct s_cfront_TraverseTable tab;
  tab.action_CallExpr[PRE_ACTION] = (void*)is_leaf_call;
  functionDefinition_is_leaf(f) = (Bool)Traverse_functionDefinition (f, &tab, (void*)TRUE);
}

/* ----------------------------------------------------------------------------
 * Add information to the expression about how it's being used
 */
static Bool
expression_info_expression (expression me, Bool is_compile_time)
{
  if (me) {
    expression_is_compile_time(me) = is_compile_time;
  }
  return is_compile_time;
}

static Bool
expression_info_SizeofExpr (SizeofExpr me)
{
  NOT_USED(me);
  return TRUE;
}

static Bool
expression_info_DeclStruct (DeclStruct me)
{
  return (DeclStruct_bf(me)!=NULL);
}

static Bool
expression_info_ArrayDecl (ArrayDecl me)
{
  if (has_static_storage (me)) return TRUE;
  return variable_length_arrays_allowed;
}

/* ----------------------------------------------------------------------------
 * Set address_taken on function designators if their value (address) is used.
 */
static expression
funcaddr_taken_IdExpr (expression me, expression call)
{
  if (!me) return NULL;
  if (!call && is_function_type (expression_type (me))) {
    declarator id = IdExpr_decl (me);
    if (id) Id_Decl_address_taken (id) = TRUE;
  }
  return NULL;
}

static expression
funcaddr_taken_CallExpr (expression me, expression call)
{
  NOT_USED (call);
  if (!me) return NULL;
  return CallExpr_func (me);
}

static expression
funcaddr_taken_ParExpr (expression me, expression call)
{
  NOT_USED (me);
  return call;
}

/* ----------------------------------------------------------------------------
 * Add simple cross links to the AST, does not do any analysis.
 */
void
_ast_cross_link (void *node,
                 functionDefinition func, statement stat, Scope scope,
		 int num,
                 void *trav(), void trav_void())
{
  NOT_USED (num);
  init_traversal ();
  SymTab_Bump_Generation ();
  
  trav_void (node, &clear_links);
  trav      (node, &link_declarations, NULL);
  trav      (node, &link_type_names, NULL);
  trav      (node, &link_external_decls, NULL);
  trav      (node, &give_statement, stat);
  trav      (node, &give_function, func);
  trav      (node, &propagate_storage_class, NoSCS);
  trav      (node, &propagate_function_specifier, NULL);
  trav      (node, &mark_function_decls, NULL);
  trav      (node, &give_FuncDecl_body, NULL);
  trav      (node, &link_enumerators, NULL);
  trav      (node, &link_initializers, NULL);
  trav      (node, &mark_initializers, NULL);
  trav_void (node, &link_callers, NULL);
  trav      (node, &expression_info, FALSE);
  
  trav      (node, &cfront_scope_actions, scope);
  trav_void (node, &cfront_defining_actions);
  trav_void (node, &cfront_applied_actions);
  
  trav_void (node, &link_tags);
  trav_void (node, &link_declarators);
  trav_void (node, &declarator_info);
}


/* ----------------------------------------------------------------------------
 * Perform trivial normalizations and add basic cross links
 */
void
_ast_decorate(void *node,
              functionDefinition func, statement stat, Scope scope,
              int num,
	      void *trav(), void trav_void())
{
  init_traversal ();
  ast_filter_initializers (node, trav_void);
  ast_translate_strings   (node, trav_void);
  ast_parse_attributes    (node, trav_void);
  _ast_cross_link         (node, func, stat, scope, num, trav, trav_void);
  trav_void (node, &eval_constants);
}

/* ----------------------------------------------------------------------------
 * Fill traverse tables
 */
static void
init_traversal(void)
{
  static Bool done = FALSE;
  if (done) return;
  done = TRUE;
  
  /* clear links */
  clear_links.action_functionDefinition [PRE_ACTION] = (void*)clear_links_functionDefinition;

  /* Evaluate constants*/
  eval_constants.action_NumExpr[PRE_ACTION]      = eval_constants_NumExpr;
  eval_constants.action_CharExpr[PRE_ACTION]     = eval_constants_CharExpr;
  eval_constants.action_WideCharExpr[PRE_ACTION] = eval_constants_WideCharExpr;

  /* Link declarations to Id_Decl's */
  link_declarations.traversal = Traverse_Down;
  link_declarations.action_declaration [PRE_ACTION]  = (void*)Return_Node;
  link_declarations.action_type_name   [PRE_ACTION]  = (void*)Return_NULL;
  link_declarations.action_declarator  [PRE_ACTION]  = (void*)link_declarations_declarator;

  /* Link type_names to Id_Decl's */
  link_type_names.traversal = Traverse_Down;
  link_type_names.action_declaration [PRE_ACTION]  = (void*)Return_NULL;
  link_type_names.action_type_name   [PRE_ACTION]  = (void*)Return_Node;
  link_type_names.action_declarator  [PRE_ACTION]  = (void*)link_type_names_declarator;

  /* Link externalDeclarations to declarations and functions */
  link_external_decls.action_externalDeclaration [PRE_ACTION] = (void*) Return_Node;
  link_external_decls.action_declaration [PRE_ACTION]         = (void*) link_external_decls_declaration;
  link_external_decls.action_functionDefinition [PRE_ACTION]  = (void*) link_external_decls_functionDefinition;
  link_external_decls.skip_children_FuncDecl                  = TRUE;
  
  /* Fill in the enclosing function */
  give_function.traversal = Traverse_Down;
  give_function.action_functionDefinition[PRE_ACTION] = (void*)Return_Node;
  give_function.action_expression[PRE_ACTION] = (void*)give_function_expression;
  give_function.action_statement[PRE_ACTION]  = (void*)give_function_statement;
  give_function.action_declarator[PRE_ACTION] = (void*)give_function_declarator;
  
  /* Fill in the enclosing statement */
  give_statement.traversal = Traverse_Down;
  give_statement.action_statement[PRE_ACTION]  = (void*)give_statement_get;
  give_statement.action_declarator[PRE_ACTION] = (void*)give_statement_declarator;
  give_statement.action_expression[PRE_ACTION] = (void*)give_statement_expression;

  /* Link breaks to the statement they belong to */
  link_breaks.traversal = Traverse_Down;
  link_breaks.action_WhileStat [PRE_ACTION]    = (void*)Return_Node;
  link_breaks.action_DoStat [PRE_ACTION]       = (void*)Return_Node;
  link_breaks.action_ForStat [PRE_ACTION]      = (void*)Return_Node;
  link_breaks.action_SwitchStat [PRE_ACTION]   = (void*)Return_Node;
  link_breaks.action_BreakStat [PRE_ACTION]    = (void*)link_breaks_BreakStat;
  
  /* Link continues to the statement they belong to */
  link_continues.traversal = Traverse_Down;
  link_continues.action_WhileStat [PRE_ACTION]    = (void*)Return_Node;
  link_continues.action_DoStat [PRE_ACTION]       = (void*)Return_Node;
  link_continues.action_ForStat [PRE_ACTION]      = (void*)Return_Node;
  link_continues.action_ContinueStat [PRE_ACTION] = (void*)link_continues_ContinueStat;

  /* Link cases and defaults to the switch statement they belong to */
  link_cases.traversal = Traverse_Down;
  link_cases.action_SwitchStat [PRE_ACTION]  = (void*)Return_Node;
  link_cases.action_CaseStat [PRE_ACTION]    = (void*)link_cases_CaseStat;
  link_cases.action_DefaultStat [PRE_ACTION] = (void*)link_cases_DefaultStat;
  
  /* Link forward declaration and uses of tagged types (structs,
     unions, and enums) to their definition */
  link_tags.action_declaration_specifier[PRE_ACTION] = link_tags_declaration_specifier;
  link_tags.action_Enum[PRE_ACTION]         = link_tags_Enum;
  link_tags.action_StructDecl[PRE_ACTION]   = link_tags_StructDecl;
  link_tags.action_StructDef[PRE_ACTION]    = link_tags_StructDef;
  link_tags.action_UnionDecl[PRE_ACTION]    = link_tags_UnionDecl;
  link_tags.action_UnionDef[PRE_ACTION]     = link_tags_UnionDef;
  link_tags.action_declaration[POST_ACTION] = link_tags_declaration;

  /* Link declarators of the same name */
  link_declarators.skip_children_StructDef     = TRUE;
  link_declarators.skip_children_UnionDef      = TRUE;
  link_declarators.action_Id_Decl [PRE_ACTION] = link_declarators_Id_Decl;
  
  /* Link enumerators of the same enum */
  link_enumerators.action_Enum[POST_ACTION]       = (void*)link_enumerators_Enum;
  link_enumerators.action_enumerator[PRE_ACTION]  = (void*)link_enumerators_enumerator;

  /* link Id_Decl to initializers */
  link_initializers.skip_children_expression = TRUE; /* Skip casts */
  link_initializers.skip_children_parameter_list = TRUE;
  link_initializers.action_Id_Decl [PRE_ACTION]         = (void*)link_initializers_Id_Decl;
  link_initializers.action_initDeclarator [POST_ACTION] = (void*)link_initializers_initDeclarator;

  /* mark initializing expressions as initializers */
  mark_initializers.traversal                           = Traverse_Down;
  mark_initializers.action_initDeclarator [PRE_ACTION]  = (void*)mark_initializers_initDeclarator;
  mark_initializers.action_expression [PRE_ACTION]      = (void*)mark_initializers_expression;

  /* link caller to functionDefinition */
  link_callers.action_CallExpr [PRE_ACTION]         = (void*)link_caller;
  link_callers.action_IdExpr [PRE_ACTION]           = (void*)link_id_caller;

  /* mark function declarators */
  mark_function_decls.traversal = Traverse_Down;
  mark_function_decls.skip_children_declaration_specifiers = TRUE;
  mark_function_decls.skip_children_parameter_list = TRUE;
  mark_function_decls.action_FuncDecl [PRE_ACTION] = (void*)Return_Node;
  mark_function_decls.action_Id_Decl [PRE_ACTION]  = (void*)mark_function_decls_Id_Decl;
  
  /* mark function declarators */
  give_FuncDecl_body.skip_children_declaration_specifiers = TRUE;
  give_FuncDecl_body.skip_children_parameter_list = TRUE;
  give_FuncDecl_body.action_functionDefinition [PRE_ACTION] = (void*)Return_Node;
  give_FuncDecl_body.action_FuncDecl [POST_ACTION] = (void*)give_FuncDecl_body_FuncDecl;
  
  /* propagate storage class to declarators */
  propagate_storage_class.action_declarator [PRE_ACTION] = (void*)propagate_storage_class_declarator;
  propagate_storage_class.action_storage_class_specifier [PRE_ACTION] = (void*)propagate_storage_class_storage_class_specifier;
  propagate_storage_class.action_function_header [PRE_ACTION] = (void*)propagate_storage_class_reset;
  propagate_storage_class.action_declaration [PRE_ACTION]     = (void*)propagate_storage_class_push;
  propagate_storage_class.action_declaration [POST_ACTION]    = (void*)propagate_storage_class_pop;
  propagate_storage_class.action_type_name [PRE_ACTION]       = (void*)propagate_storage_class_reset;
  propagate_storage_class.skip_children_StructDef = TRUE;
  propagate_storage_class.skip_children_UnionDef  = TRUE;
  propagate_storage_class.skip_children_UnionDef  = TRUE;

  /* propagate function specifier to declarators */
  propagate_function_specifier.action_declarator [PRE_ACTION] = (void*)propagate_function_specifier_declarator;
  propagate_function_specifier.action_declaration_specifier [PRE_ACTION] = (void*)propagate_function_specifier_function_specifier;
  propagate_function_specifier.action_function_header [PRE_ACTION] = (void*)propagate_function_specifier_reset;
  propagate_function_specifier.action_declaration [PRE_ACTION]     = (void*)propagate_function_specifier_reset;
  propagate_function_specifier.action_type_name [PRE_ACTION]       = (void*)propagate_function_specifier_reset;
  propagate_function_specifier.skip_children_StructDef = TRUE;
  propagate_function_specifier.skip_children_UnionDef  = TRUE;

  /* add information to declarators about how they are being used */
  declarator_info.action_functionDefinition[PRE_ACTION] = declarator_info_functionDefinition;
  declarator_info.action_mon_expression    [PRE_ACTION] = declarator_info_mon_expression;
  
  /* add information to expression about how they are being used */
  expression_info.action_expression [PRE_ACTION] = (void*)expression_info_expression;
  expression_info.action_SizeofExpr [PRE_ACTION] = (void*)expression_info_SizeofExpr;
  expression_info.action_DeclStruct [PRE_ACTION] = (void*)expression_info_DeclStruct;
  expression_info.action_ArrayDecl  [PRE_ACTION] = (void*)expression_info_ArrayDecl;

  /* this traversal must be used after typing! */
  /* set address_taken on function designators that are used in places other than function calls */
  funcaddr_taken.action_IdExpr   [PRE_ACTION] = (void*)funcaddr_taken_IdExpr;
  funcaddr_taken.action_CallExpr [PRE_ACTION] = (void*)funcaddr_taken_CallExpr;
  funcaddr_taken.action_ParExpr  [PRE_ACTION] = (void*)funcaddr_taken_ParExpr;
}


/* ----------------------------------------------------------------------------
 * Set address_taken on function designators used in places other than function calls.
 */
void
cfront_mark_functionpointers(cfront root)
{
  init_traversal ();
  Traverse_cfront (root, &funcaddr_taken, NULL);
}

/* ----------------------------------------------------------------------------
 * Decorate the root
 */
void
cfront_decorate(cfront root)
{
  ast_decorate(cfront, root, NULL, NULL, cfront_scope(root));

  /* The following must be done from the top level of function bodies or higher. */
  Traverse_cfront (root, &link_cases, NULL);
  Traverse_cfront (root, &link_breaks, NULL);
  Traverse_cfront (root, &link_continues, NULL);
}

/* ----------------------------------------------------------------------------
 * Remove old symbols from the symbol table
 */
void
cfront_purge_namespaces (void)
{
  SymTab_Bump_Generation ();
  Clean_namespaces (cfront_namespaces[0], cfront_NUM_NAMESPACES,
                    symbol_table_bottom);
}

/* ----------------------------------------------------------------------------
 * Set the bottom of the symbol table, below this generation will not
 * be purged
 */
void
cfront_set_symtab_bottom(void)
{
  SymTab_Bump_Generation ();
  symbol_table_bottom = SymTab_Generation ();
}

/* ----------------------------------------------------------------------------
 * A function body uses the scope of the parameters
 */
static void *FindResult;

static void *
find_node (void *n, void *p)
{
  NOT_USED (p);
  FindResult = n;
  return NULL;
}

static void *
function_body_scope(function_header me, void *val)
{
  static struct s_cfront_TraverseTable find;
  parameter_list ps;
  NOT_USED (val);

  find.traversal                          = Traverse_Stop;
  find.action_parameter_list [PRE_ACTION] = (void*)find_node;
  Traverse_function_header (me, &find, me);
  ps = FindResult;
  
  return parameter_list_scope (ps);
}

/* ----------------------------------------------------------------------------
 * Initialize cfront
 */
void
cfront_pre_initialize (void)
{  
  static Bool done = FALSE;
  if (done) return;
  done = TRUE;
  
  CPP_initialize ();
  find_location = CPP_find_location;
  
  Extend_namespaces (cfront_NUM_NAMESPACES, cfront_namespaces);
  Make_identifier_table ();
  
  init_skip_table ();
  /* cfront_copy_actions.clear_back_edges = TRUE; */
  init_expr_tables ();
  init_stat_tables ();

  cfront_scope_actions.action_function_header [POST_ACTION] = function_body_scope;
  
  init_AST_remove (&cfront_RemoveTable, (void*)copy_cfront_fill_table);
  cfront_RemoveOffset = (void*)&AST_remove(cfront_root) - (void*)cfront_root;
  
  init_AST_replace (&cfront_ReplaceTable, (void*)copy_cfront_fill_table);
  cfront_ReplaceOffset = (void*)&AST_replace(cfront_root) - (void*)cfront_root;

  default_integral_ranges (); /* Use host_integral_ranges() if you are compiling for a different platform. */
  CPP_predefines (1); /* Set standard defines except __STDC_VERSION__ as flags have not been parsed yet. */
  egg_exit_on_errors ();
}

void
cfront_post_initialize (void)
{  
  static Bool done = FALSE;
  if (done) return;
  done = TRUE;
  
  initialize_types ();
  cfront_set_symtab_bottom();
}

void
cfront_initialize (void)
{  
  cfront_pre_initialize ();
  cfront_post_initialize ();
}

void
include_limits_h (const char *filename)
{
  CPP_preprocess ("limits.h");
  host_integral_ranges ((char *) filename);
}

/* ----------------------------------------------------------------------------
 * Set defines and system include paths, and parse limits.h. Needed if the
 * application uses standard header files. Call after parse_flags().
 */
void
cfront_stdclib_support (const char *filename)
{
  CPP_predefines (1); /* If -c99 given, set __STDC_VERSION__. */
  CPP_std_include_path ();
  CPP_preprocess ("limits.h");
  host_integral_ranges ((char *) filename);
}

/* ----------------------------------------------------------------------------
 * Parse filename
 */
cfront
cfront_Parse (const char *filename)
{
  extern int cfront_restart (FILE *);
  extern int cfront_parse (void);
  extern FILE *cfront_in;

  if (!CPP_preprocess(filename)) return NULL;
  CPP_set_location (input_size);

  if ( pp_only ) {
    if (print_file) CPP_emit(print_file);
    else            CPP_emit(stdout);
    return NULL;
  }
  cfront_in = NULL;
  if (filename) {
    scan_init ((char*)filename);
  } else {
    scan_init ("<stdin>");
  }
  cfront_restart (cfront_in);
  is_parsing = TRUE;
  cfront_parse ();
  is_parsing = FALSE;
  if(NumErrors>0) return NULL;
  cfront_decorate(cfront_root);

  /* after this point only allow fatal semantical errors */
  /* set_semantical_mode (egg_disabled_kind); why? I'd like to see my typing warnings as well */
  
  return cfront_root;
}

void
cfront_set_pp_only(Bool pp)
{
  pp_only = pp;
}


void 
print_cfront_root (void)
{
  Print_cfront (cfront_root);
}
